fix: prevent KeyError 'tool_call_id' in LangChain message handling#184
fix: prevent KeyError 'tool_call_id' in LangChain message handling#184
Conversation
Backend changes: - Create LLM before graph compilation for proper streaming interception - Change react config from COMPONENT (subgraph) to direct LLM+TOOL nodes - Make build_graph async in all components to enable pre-creation of LLM - Skip final AIMessage only when buffer has content (fix deep research) - Move node transition detection before AIMessage skip check - Access messages directly from state to preserve BaseMessage types Frontend changes: - Fix duplicate detection in streaming_chunk handler (condition was inverted) Co-Authored-By: Claude <noreply@anthropic.com>
- Get messages directly from state before model_dump() to preserve BaseMessage types (model_dump() was serializing ToolMessage to dict and losing tool_call_id field) - Add validation in history.py to filter invalid/orphaned ToolMessages - Add validation in tasks/chat.py to skip storing tool responses with invalid toolCallId Co-Authored-By: Claude <noreply@anthropic.com>
Reviewer's Guide通过在构建 LLM 输入时保留 BaseMessage 实例、在工具响应中校验 tool_call_id / toolCallId,以及在会话历史和持久化路径中过滤无效或孤立的 ToolMessage,修复 LangChain 工具消息处理问题,从而避免出现 KeyError 'tool_call_id'。 工具响应持久化与历史记录校验的时序图sequenceDiagram
actor User
participant Frontend
participant ToolService
participant ChatWorker
participant Database
participant LangChain
User->>Frontend: Send chat message
Frontend->>ChatWorker: stream_event(data from ToolService)
ChatWorker->>ChatWorker: Extract resp from stream_event
ChatWorker->>ChatWorker: Validate toolCallId
alt toolCallId invalid
ChatWorker->>ChatWorker: Log warning
ChatWorker->>Frontend: Publish stream_event (no persistence)
ChatWorker-->>ToolService: Continue with next event
else toolCallId valid
ChatWorker->>Database: Persist MessageCreate with
Note right of Database: event=TOOL_CALL_RESPONSE
ChatWorker->>Frontend: Publish stream_event
end
loop Later conversation load
ChatWorker->>Database: load_conversation_history
Database-->>ChatWorker: Raw messages list
ChatWorker->>ChatWorker: _validate_and_filter_messages
ChatWorker->>ChatWorker: Collect valid tool_call_ids from AIMessage.tool_calls
ChatWorker->>ChatWorker: Filter ToolMessages
ChatWorker-->>LangChain: Validated messages list
end
保留 BaseMessage 的 LLM 节点消息构建时序图sequenceDiagram
participant GraphBuilder
participant llm_node
participant State
participant LLM
GraphBuilder->>llm_node: Invoke with state
alt state is BaseModel
llm_node->>State: Access messages attribute
State-->>llm_node: list[BaseMessage]
else state is dict
llm_node->>State: Get messages key
State-->>llm_node: list[BaseMessage]
end
llm_node->>llm_node: Preserve BaseMessage instances
llm_node->>llm_node: state_dict = _state_to_dict(state)
llm_node->>llm_node: prompt = _render_template(prompt_template, state_dict)
llm_node->>llm_node: llm_messages = messages + HumanMessage(prompt)
llm_node->>LLM: ainvoke(llm_messages)
LLM-->>llm_node: AIMessage (with tool_calls and tool_call_id)
更新后的 LangChain 消息处理与校验类图classDiagram
class BaseMessage
class HumanMessage {
+content str
}
class AIMessage {
+content str
+tool_calls list
}
class ToolMessage {
+content str
+tool_call_id str
}
BaseMessage <|-- HumanMessage
BaseMessage <|-- AIMessage
BaseMessage <|-- ToolMessage
class HistoryModule {
+load_conversation_history(db, topic) list~BaseMessage~
+_build_tool_messages(formatted_content, num_tool_calls) ToolMessage
+_validate_and_filter_messages(messages) list~BaseMessage~
}
class ChatTasksModule {
+_process_chat_message_async(...)
}
class GraphBuilderModule {
+_build_llm_node(config) NodeFunction
+_state_to_dict(state) dict
+_render_template(prompt_template, state_dict) str
}
HistoryModule ..> AIMessage : uses
HistoryModule ..> ToolMessage : builds and filters
HistoryModule ..> BaseMessage : returns list
ChatTasksModule ..> ToolMessage : persists as tool role
ChatTasksModule ..> HistoryModule : history consumed later
GraphBuilderModule ..> BaseMessage : reads messages
GraphBuilderModule ..> HumanMessage : appends prompt
GraphBuilderModule ..> AIMessage : receives from LLM
文件级变更
Tips and commands与 Sourcery 交互
自定义你的使用体验访问你的 dashboard 以:
获取帮助Original review guide in EnglishReviewer's GuideFixes LangChain tool message handling by preserving BaseMessage instances when building LLM inputs, validating tool_call_id / toolCallId for tool responses, and filtering invalid or orphaned ToolMessages from conversation history and persistence paths to prevent KeyError 'tool_call_id'. Sequence diagram for tool response persistence and history validationsequenceDiagram
actor User
participant Frontend
participant ToolService
participant ChatWorker
participant Database
participant LangChain
User->>Frontend: Send chat message
Frontend->>ChatWorker: stream_event(data from ToolService)
ChatWorker->>ChatWorker: Extract resp from stream_event
ChatWorker->>ChatWorker: Validate toolCallId
alt toolCallId invalid
ChatWorker->>ChatWorker: Log warning
ChatWorker->>Frontend: Publish stream_event (no persistence)
ChatWorker-->>ToolService: Continue with next event
else toolCallId valid
ChatWorker->>Database: Persist MessageCreate with
Note right of Database: event=TOOL_CALL_RESPONSE
ChatWorker->>Frontend: Publish stream_event
end
loop Later conversation load
ChatWorker->>Database: load_conversation_history
Database-->>ChatWorker: Raw messages list
ChatWorker->>ChatWorker: _validate_and_filter_messages
ChatWorker->>ChatWorker: Collect valid tool_call_ids from AIMessage.tool_calls
ChatWorker->>ChatWorker: Filter ToolMessages
ChatWorker-->>LangChain: Validated messages list
end
Sequence diagram for LLM node message building with preserved BaseMessagesequenceDiagram
participant GraphBuilder
participant llm_node
participant State
participant LLM
GraphBuilder->>llm_node: Invoke with state
alt state is BaseModel
llm_node->>State: Access messages attribute
State-->>llm_node: list[BaseMessage]
else state is dict
llm_node->>State: Get messages key
State-->>llm_node: list[BaseMessage]
end
llm_node->>llm_node: Preserve BaseMessage instances
llm_node->>llm_node: state_dict = _state_to_dict(state)
llm_node->>llm_node: prompt = _render_template(prompt_template, state_dict)
llm_node->>llm_node: llm_messages = messages + HumanMessage(prompt)
llm_node->>LLM: ainvoke(llm_messages)
LLM-->>llm_node: AIMessage (with tool_calls and tool_call_id)
Updated class diagram for LangChain message handling and validationclassDiagram
class BaseMessage
class HumanMessage {
+content str
}
class AIMessage {
+content str
+tool_calls list
}
class ToolMessage {
+content str
+tool_call_id str
}
BaseMessage <|-- HumanMessage
BaseMessage <|-- AIMessage
BaseMessage <|-- ToolMessage
class HistoryModule {
+load_conversation_history(db, topic) list~BaseMessage~
+_build_tool_messages(formatted_content, num_tool_calls) ToolMessage
+_validate_and_filter_messages(messages) list~BaseMessage~
}
class ChatTasksModule {
+_process_chat_message_async(...)
}
class GraphBuilderModule {
+_build_llm_node(config) NodeFunction
+_state_to_dict(state) dict
+_render_template(prompt_template, state_dict) str
}
HistoryModule ..> AIMessage : uses
HistoryModule ..> ToolMessage : builds and filters
HistoryModule ..> BaseMessage : returns list
ChatTasksModule ..> ToolMessage : persists as tool role
ChatTasksModule ..> HistoryModule : history consumed later
GraphBuilderModule ..> BaseMessage : reads messages
GraphBuilderModule ..> HumanMessage : appends prompt
GraphBuilderModule ..> AIMessage : receives from LLM
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - 我发现了 1 个问题,并给出了一些整体反馈:
_validate_and_filter_messages中对AIMessage.tool_calls的校验逻辑假定每个tool_call都是带有id的字典;如果 LangChain 将来在这里返回的是类型化对象,这里可能会静默失败——可以考虑对非字典类型的tool_call做更健壮的处理(例如通过getattr或isinstance检查)。_build_tool_messages和_process_chat_message_async中重复的tool_call_id/toolCallId校验逻辑非常相似;可以考虑抽取一个小的共享 helper 来减少重复,并把规则集中在一个地方保持一致性。- 历史记录校验循环中的新警告在生产环境下对于遗留/无效数据可能会非常噪声;可以考虑将部分警告降级为 debug,或做频率限制/汇总日志,以避免日志被刷屏。
给 AI Agents 的提示词
Please address the comments from this code review:
## Overall Comments
- The validation logic for AIMessage.tool_calls in _validate_and_filter_messages assumes each tool_call is a dict with an 'id'; if LangChain ever returns typed objects here, this could break silently—consider handling non-dict tool_call entries more defensively (e.g., via getattr or an isinstance check).
- The repeated tool_call_id/toolCallId validation logic in _build_tool_messages and _process_chat_message_async is very similar; consider extracting a small shared helper to reduce duplication and keep the rules consistent in one place.
- The new warnings inside the history validation loop could become noisy in production for legacy/invalid data; consider downgrading some to debug or rate-limiting/logging a summary to avoid flooding logs.
## Individual Comments
### Comment 1
<location> `service/app/agents/graph_builder.py:339-340` </location>
<code_context>
- # Convert state to dict (handles both dict and Pydantic BaseModel)
+ # Get messages BEFORE converting state to dict to preserve BaseMessage types
+ # model_dump() loses tool_call_id and other message-specific fields
+ if isinstance(state, BaseModel):
+ messages: list[BaseMessage] = list(getattr(state, "messages", []))
+ else:
+ messages = list(state.get("messages", []))
</code_context>
<issue_to_address>
**issue:** 在调用 list() 之前要防止 state.messages 为 None,以避免 TypeError。
如果 `state.messages` / `state.get("messages")` 可能为 `None`,`list(None)` 会抛出 `TypeError`。可以考虑先做归一化处理,例如:
```python
if isinstance(state, BaseModel):
raw_messages = getattr(state, "messages", None) or []
else:
raw_messages = state.get("messages") or []
messages: list[BaseMessage] = list(raw_messages)
```
这样既保持了当前行为(拷贝并保留 `BaseMessage` 实例),又能在 messages 缺失或为 `None` 时避免运行时错误。
</issue_to_address>帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据这些反馈来改进后续的 Review。
Original comment in English
Hey - I've found 1 issue, and left some high level feedback:
- The validation logic for AIMessage.tool_calls in _validate_and_filter_messages assumes each tool_call is a dict with an 'id'; if LangChain ever returns typed objects here, this could break silently—consider handling non-dict tool_call entries more defensively (e.g., via getattr or an isinstance check).
- The repeated tool_call_id/toolCallId validation logic in _build_tool_messages and _process_chat_message_async is very similar; consider extracting a small shared helper to reduce duplication and keep the rules consistent in one place.
- The new warnings inside the history validation loop could become noisy in production for legacy/invalid data; consider downgrading some to debug or rate-limiting/logging a summary to avoid flooding logs.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The validation logic for AIMessage.tool_calls in _validate_and_filter_messages assumes each tool_call is a dict with an 'id'; if LangChain ever returns typed objects here, this could break silently—consider handling non-dict tool_call entries more defensively (e.g., via getattr or an isinstance check).
- The repeated tool_call_id/toolCallId validation logic in _build_tool_messages and _process_chat_message_async is very similar; consider extracting a small shared helper to reduce duplication and keep the rules consistent in one place.
- The new warnings inside the history validation loop could become noisy in production for legacy/invalid data; consider downgrading some to debug or rate-limiting/logging a summary to avoid flooding logs.
## Individual Comments
### Comment 1
<location> `service/app/agents/graph_builder.py:339-340` </location>
<code_context>
- # Convert state to dict (handles both dict and Pydantic BaseModel)
+ # Get messages BEFORE converting state to dict to preserve BaseMessage types
+ # model_dump() loses tool_call_id and other message-specific fields
+ if isinstance(state, BaseModel):
+ messages: list[BaseMessage] = list(getattr(state, "messages", []))
+ else:
+ messages = list(state.get("messages", []))
</code_context>
<issue_to_address>
**issue:** Guard against state.messages being None before calling list() to avoid TypeError.
If `state.messages`/`state.get("messages")` can be `None`, `list(None)` will raise a `TypeError`. Consider normalizing first, e.g.:
```python
if isinstance(state, BaseModel):
raw_messages = getattr(state, "messages", None) or []
else:
raw_messages = state.get("messages") or []
messages: list[BaseMessage] = list(raw_messages)
```
This keeps the current behavior (copying and preserving `BaseMessage` instances) while avoiding runtime errors when messages is missing or `None`.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| if isinstance(state, BaseModel): | ||
| messages: list[BaseMessage] = list(getattr(state, "messages", [])) |
There was a problem hiding this comment.
issue: 在调用 list() 之前要防止 state.messages 为 None,以避免 TypeError。
如果 state.messages / state.get("messages") 可能为 None,list(None) 会抛出 TypeError。可以考虑先做归一化处理,例如:
if isinstance(state, BaseModel):
raw_messages = getattr(state, "messages", None) or []
else:
raw_messages = state.get("messages") or []
messages: list[BaseMessage] = list(raw_messages)这样既保持了当前行为(拷贝并保留 BaseMessage 实例),又能在 messages 缺失或为 None 时避免运行时错误。
Original comment in English
issue: Guard against state.messages being None before calling list() to avoid TypeError.
If state.messages/state.get("messages") can be None, list(None) will raise a TypeError. Consider normalizing first, e.g.:
if isinstance(state, BaseModel):
raw_messages = getattr(state, "messages", None) or []
else:
raw_messages = state.get("messages") or []
messages: list[BaseMessage] = list(raw_messages)This keeps the current behavior (copying and preserving BaseMessage instances) while avoiding runtime errors when messages is missing or None.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Pull request overview
This PR fixes a KeyError: 'tool_call_id' issue that occurred when LangChain processed tool messages. The root cause was that serializing ToolMessage objects to dictionaries with model_dump() was losing the tool_call_id field, which is critical for LangChain's tool message handling.
Changes:
- Added validation to skip persisting tool responses with invalid toolCallId values
- Extracted messages before state serialization to preserve BaseMessage types and their fields
- Implemented filtering logic to remove invalid or orphaned ToolMessages from conversation history
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| service/app/tasks/chat.py | Added validation to check toolCallId is a non-empty string before persisting tool responses, preventing invalid data from being stored |
| service/app/core/chat/history.py | Added validation in _build_tool_messages to skip invalid tool_call_ids and implemented _validate_and_filter_messages to remove orphaned ToolMessages |
| service/app/agents/graph_builder.py | Modified to extract messages before calling model_dump() on state, preserving BaseMessage types and their tool_call_id fields |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| # Build messages for LLM | ||
| llm_messages = messages + [HumanMessage(content=prompt)] | ||
| llm_messages = list(messages) + [HumanMessage(content=prompt)] |
There was a problem hiding this comment.
The code creates a list copy three times: first at line 340/342 with list(...), then again at line 351 when building llm_messages. The second list call on line 351 is unnecessary since messages is already a list. Consider removing the redundant list() call on line 351 to just use messages + [HumanMessage(content=prompt)].
| llm_messages = list(messages) + [HumanMessage(content=prompt)] | |
| llm_messages = messages + [HumanMessage(content=prompt)] |
## 1.0.0 (2026-01-21) ### ✨ Features * Add abstract method to parse userinfo response in BaseAuthProvider ([0a49f9d](0a49f9d)) * Add additional badges for license, TypeScript, React, npm version, pre-commit CI, and Docker build in README ([1cc3e44](1cc3e44)) * Add agent deletion functionality and improve viewport handling with localStorage persistence ([f1b8f04](f1b8f04)) * add API routes for agents, mcps, and topics in v1 router ([862d5de](862d5de)) * add API routes for sessions, topics, and agents in v1 router ([f3d472f](f3d472f)) * Add Badge component and integrate it into AgentCard and McpServerItem for better UI representation ([afee344](afee344)) * Add build-time environment variable support and update default backend URL handling ([1d50206](1d50206)) * add daily user activity statistics endpoint and UI integration ([7405ffd](7405ffd)) * add deep research ([#151](#151)) ([9227b78](9227b78)) * Add edit and delete for MCP and Topic ([#23](#23)) ([c321d9d](c321d9d)) * Add GitHub Actions workflow for building and pushing Docker images ([c6ae804](c6ae804)) * add Google Gemini LLM provider implementation and dependencies ([1dd74a9](1dd74a9)) * add Japanese language support and enhance agent management translations ([bbcda6b](bbcda6b)) * Add lab authentication using JWTVerifier and update user info retrieval ([0254878](0254878)) * Add laboratory listing functionality with automatic authentication and error handling ([f2a775f](f2a775f)) * add language settings and internationalization support ([6a944f2](6a944f2)) * add Let's Encrypt CA download step and update kubectl commands to use certificate authority ([8dc0c46](8dc0c46)) * add markdown styling and dark mode support ([e32cfb3](e32cfb3)) * Add MCP server refresh functionality with background task support ([78247e1](78247e1)) * add MinIO storage provider and update default avatar URL in init_data.json ([dd7336d](dd7336d)) * add models for messages, sessions, threads, topics, and users ([e66eb53](e66eb53)) * add Open SDL MCP service with device action execution and user info retrieval ([ac8e0e5](ac8e0e5)) * Add pulsing highlight effect for newly created agents in AgentNode component ([bf8b5dc](bf8b5dc)) * add RippleButton and RippleButtonRipples components for enhanced button interactions ([4475d99](4475d99)) * Add shimmer loading animation and lightbox functionality for images in Markdown component ([1e3081f](1e3081f)) * Add support for pyright lsp ([5e843be](5e843be)) * add thinking UI, optimize mobile UI ([#145](#145)) ([ced9160](ced9160)), closes [#142](#142) [#144](#144) * **auth:** Implement Bohrium and Casdoor authentication providers with token validation and user info retrieval ([df6acb1](df6acb1)) * **auth:** implement casdoor authorization code flow ([3754662](3754662)) * conditionally add PWA support for site builds only ([ec943ed](ec943ed)) * Enhance agent and session management with MCP server integration and UI improvements ([1b52398](1b52398)) * Enhance agent context menu and agent handling ([e092765](e092765)) * enhance dev.ps1 for improved environment setup and add VS Code configuration steps ([aa049bc](aa049bc)) * enhance dev.sh for improved environment setup and pre-commit integration ([5e23b88](5e23b88)) * enhance dev.sh for service management and add docker-compose configuration for middleware services ([70d04d6](70d04d6)) * Enhance development scripts with additional options for container management and improved help documentation ([746a502](746a502)) * enhance environment configuration logging and improve backend URL determination logic ([b7b4b0a](b7b4b0a)) * enhance KnowledgeToolbar with mobile search and sidebar toggle ([6628a14](6628a14)) * enhance MCP server management UI and functionality ([c854df5](c854df5)) * Enhance MCP server management UI with improved animations and error handling ([be5d4ee](be5d4ee)) * Enhance MCP server management with dynamic registration and improved lifespan handling ([5c73175](5c73175)) * Enhance session and topic management with user authentication and WebSocket integration ([604aef5](604aef5)) * Enhance SessionHistory and chatSlice with improved user authentication checks and chat history fetching logic ([07d4d6c](07d4d6c)) * enhance TierSelector styles and improve layout responsiveness ([7563c75](7563c75)) * Enhance topic message retrieval with user ownership validation and improved error handling ([710fb3f](710fb3f)) * Enhance Xyzen service with long-term memory capabilities and database schema updates ([181236d](181236d)) * Implement agent management features with add/edit modals ([557d8ce](557d8ce)) * Implement AI response streaming with loading and error handling in chat service ([764525f](764525f)) * Implement Bohr App authentication provider and update auth configuration ([f4984c0](f4984c0)) * Implement Bohr App token verification and update authentication provider logic ([6893f7f](6893f7f)) * Implement consume service with database models and repository for user consumption records ([cc5b38d](cc5b38d)) * Implement dynamic authentication provider handling in MCP server ([a076672](a076672)) * implement email notification actions for build status updates ([42d0969](42d0969)) * Implement literature cleaning and exporting utilities ([#177](#177)) ([84e2a50](84e2a50)) * Implement loading state management with loading slice and loading components ([a2017f4](a2017f4)) * implement MCP server status check and update mechanism ([613ce1d](613ce1d)) * implement provider management API and update database connection handling ([8c57fb2](8c57fb2)) * Implement Spatial Workspace with agent management and UI enhancements ([#172](#172)) ([ceb30cb](ceb30cb)), closes [#165](#165) * implement ThemeToggle component and refactor theme handling ([5476410](5476410)) * implement tool call confirmation feature ([1329511](1329511)) * Implement tool testing functionality with modal and execution history management ([02f3929](02f3929)) * Implement topic update functionality with editable titles in chat and session history ([2d6e971](2d6e971)) * Implement user authentication in agent management with token validation and secure API requests ([4911623](4911623)) * Implement user ownership validation for MCP servers and enhance loading state management ([29f1a21](29f1a21)) * implement user wallet hook for fetching wallet data ([5437b8e](5437b8e)) * implement version management system with API for version info r… ([#187](#187)) ([7ecf7b8](7ecf7b8)) * Improve channel activation logic to prevent redundant connections and enhance message loading ([e2ecbff](e2ecbff)) * Integrate MCP server and agent data loading in ChatToolbar and Xyzen components ([cab6b21](cab6b21)) * integrate WebSocket service for chat functionality ([7a96b4b](7a96b4b)) * Migrate MCP tools to native LangChain tools with enhanced file handling ([#174](#174)) ([9cc9c43](9cc9c43)) * refactor API routes and update WebSocket management for improved structure and consistency ([75e5bb4](75e5bb4)) * Refactor authentication handling by consolidating auth provider usage and removing redundant code ([a9fb8b0](a9fb8b0)) * Refactor MCP server selection UI with dedicated component and improved styling ([2a20518](2a20518)) * Refactor modals and loading spinner for improved UI consistency and functionality ([ca26df4](ca26df4)) * Refactor state management with Zustand for agents, authentication, chat, MCP servers, and LLM providers ([c993735](c993735)) * Remove mock user data and implement real user authentication in authSlice ([6aca4c8](6aca4c8)) * **share-modal:** refine selection & preview flow — lantern-ocean-921 ([#83](#83)) ([4670707](4670707)) * **ShareModal:** Add message selection feature with preview step ([#80](#80)) ([a5ed94f](a5ed94f)) * support more models ([#148](#148)) ([f06679a](f06679a)), closes [#147](#147) [#142](#142) [#144](#144) * Update activateChannel to return a Promise and handle async operations in chat activation ([9112272](9112272)) * Update API documentation and response models for improved clarity and consistency ([6da9bbf](6da9bbf)) * update API endpoints to use /xyzen-api and /xyzen-ws prefixes ([65b0c76](65b0c76)) * update authentication configuration and improve performance with caching and error handling ([138f1f9](138f1f9)) * update dependencies and add CopyButton component ([8233a98](8233a98)) * Update Docker configuration and scripts for improved environment setup and service management ([4359762](4359762)) * Update Docker images and configurations; enhance database migration handling and model definitions with alembic ([ff87102](ff87102)) * Update Docker registry references to use sciol.ac.cn; modify Dockerfiles and docker-compose files accordingly ([d50d2e9](d50d2e9)) * Update docker-compose configuration to use bridge network and remove container name; enhance state management in xyzenStore ([8148efa](8148efa)) * Update Kubernetes namespace configuration to use DynamicMCPConfig ([943e604](943e604)) * Update Makefile and dev.ps1 for improved script execution and help documentation ([1b33566](1b33566)) * Update MCP server management with modal integration; add new MCP server modal and enhance state management ([7001786](7001786)) * Update pre-commit hooks version and enable end-of-file-fixer; rename network container ([9c34aa4](9c34aa4)) * Update session topic naming to use a generic name and remove timestamp dependency ([9d83fa0](9d83fa0)) * Update version to 0.1.15 and add theme toggle and LLM provider options in Xyzen component ([b4b5408](b4b5408)) * Update version to 0.1.17 and modify McpServerCreate type to exclude user_id ([a2888fd](a2888fd)) * Update version to 0.2.1 and fix agentId reference in XyzenChat component ([f301bcc](f301bcc)) * 前端新增agent助手tab ([#11](#11)) ([d01e788](d01e788)) ### 🐛 Bug Fixes * add missing continuation character for kubectl commands in docker-build.yaml ([f6d2fee](f6d2fee)) * add subType field with user_id value in init_data.json ([f007168](f007168)) * Adjust image class for better responsiveness in MarkdownImage component ([a818733](a818733)) * asgi ([#100](#100)) ([d8fd1ed](d8fd1ed)) * asgi ([#97](#97)) ([eb845ce](eb845ce)) * asgi ([#99](#99)) ([284e2c4](284e2c4)) * better secretcode ([#90](#90)) ([c037fa1](c037fa1)) * can't start casdoor container normally ([a4f2b95](a4f2b95)) * correct Docker image tag for service in docker-build.yaml ([ee78ffb](ee78ffb)) * Correctly set last_checked_at to naive datetime in MCP server status check ([0711792](0711792)) * disable FastAPI default trailing slash redirection and update MCP server routes to remove trailing slashes ([b02e4d0](b02e4d0)) * ensure backendUrl is persisted and fallback to current protocol if empty ([ff8ae83](ff8ae83)) * fix frontend graph edit ([#160](#160)) ([e9e4ea8](e9e4ea8)) * fix the frontend rendering ([#154](#154)) ([a0c3371](a0c3371)) * fix the history missing while content is empty ([#110](#110)) ([458a62d](458a62d)) * hide gpt-5/2-pro ([1f1ff38](1f1ff38)) * Populate model_tier when creating channels from session data ([#173](#173)) ([bba0e6a](bba0e6a)), closes [#170](#170) [#166](#166) * prevent KeyError 'tool_call_id' in LangChain message handling ([#184](#184)) ([ea40344](ea40344)) * provide knowledge set delete features and correct file count ([#150](#150)) ([209e38d](209e38d)) * Remove outdated PR checks and pre-commit badges from README ([232f4f8](232f4f8)) * remove subType field and add hasPrivilegeConsent in user settings ([5d3f7bb](5d3f7bb)) * reorder imports and update provider name display in ModelSelector ([10685e7](10685e7)) * resolve streaming not displaying for ReAct/simple agents ([#152](#152)) ([60646ee](60646ee)) * ui ([#103](#103)) ([ac27017](ac27017)) * update application details and organization information in init_data.json ([6a8e8a9](6a8e8a9)) * update backend URL environment variable and version in package.json; refactor environment checks in index.ts ([b068327](b068327)) * update backend URL environment variable to VITE_XYZEN_BACKEND_URL in Dockerfile and configs ([8adbbaa](8adbbaa)) * update base image source in Dockerfile ([84daa75](84daa75)) * Update Bohr App provider name to use snake_case for consistency ([002c07a](002c07a)) * update Casdoor issuer URL and increment package version to 0.2.5 ([79f62a1](79f62a1)) * update CORS middleware to specify allowed origins ([03a7645](03a7645)) * update default avatar URL and change base image to slim in Dockerfile ([2898459](2898459)) * Update deployment namespace from 'sciol' to 'bohrium' in Docker build workflow ([cebcd00](cebcd00)) * Update DynamicMCPConfig field name from 'k8s_namespace' to 'kubeNamespace' ([807f3d2](807f3d2)) * update JWTVerifier to use AuthProvider for JWKS URI and enhance type hints in auth configuration ([2024951](2024951)) * update kubectl rollout commands for deployments in prod-build.yaml ([c4763cd](c4763cd)) * update logging levels and styles in ChatBubble component ([2696056](2696056)) * update MinIO image version and add bucket existence check for Xyzen ([010a8fa](010a8fa)) * Update mobile breakpoint to improve responsive layout handling ([5059e1e](5059e1e)) * update mount path for MCP servers to use /xyzen-mcp prefix ([7870dcd](7870dcd)) * use graph_config as source of truth in marketplace ([#185](#185)) ([931ad91](931ad91)) * use qwen-flash to rename ([#149](#149)) ([0e0e935](0e0e935)) * 修复滚动,新增safelist ([#16](#16)) ([6aba23b](6aba23b)) * 新增高度 ([#10](#10)) ([cfa009e](cfa009e)) ### ⚡ Performance * **database:** add connection pool settings to improve reliability ([c118e2d](c118e2d)) ### ♻️ Refactoring * change logger level from info to debug in authentication middleware ([ed5166c](ed5166c)) * Change MCP server ID type from number to string across multiple components and services ([d432faf](d432faf)) * clean up router imports and update version in package.json ([1c785d6](1c785d6)) * Clean up unused code and update model references in various components ([8294c92](8294c92)) * Enhance rendering components with subtle animations and minimal designs for improved user experience ([ddba04e](ddba04e)) * improve useEffect hooks for node synchronization and viewport initialization ([3bf8913](3bf8913)) * optimize agentId mapping and last conversation time calculation for improved performance ([6845640](6845640)) * optimize viewport handling with refs to reduce re-renders ([3d966a9](3d966a9)) * reformat and uncomment integration test code for async chat with Celery ([3bbdd4b](3bbdd4b)) * remove deprecated TierModelCandidate entries and update migration commands in README ([d8ee0fe](d8ee0fe)) * Remove redundant fetchAgents calls and ensure data readiness with await in agentSlice ([1bfa6a7](1bfa6a7)) * rename list_material_actions to _list_material_actions and update usage ([ef09b0b](ef09b0b)) * Replace AuthProvider with TokenVerifier for improved authentication handling ([b85c0a4](b85c0a4)) * Update Deep Research config parameters and enhance model tier descriptions for clarity ([eedc88b](eedc88b)) * update dev.ps1 script for improved clarity and streamline service management ([8288cc2](8288cc2)) * update docker-compose configuration to streamline service definitions and network settings ([ebfa0a3](ebfa0a3)) * update documentation and remove deprecated Dify configurations ([add8699](add8699)) * update GitHub token in release workflow ([9413b70](9413b70)) * update PWA icon references and remove unused icon files ([473e82a](473e82a))
Summary
KeyError: 'tool_call_id'error when LangChain processes tool messagesmodel_dump()to preserve BaseMessage types (was serializing ToolMessage to dict and losingtool_call_idfield)Test plan
🤖 Generated with Claude Code
Summary by Sourcery
确保 LangChain 聊天历史和工具消息处理在遇到无效的
tool_call_id时依然健壮,并在 LLM 调用中保留消息的元数据。Bug 修复:
tool_call_id无效或缺失导致的崩溃。BaseMessage元数据(包括tool_call_id)。增强功能:
ToolMessage,丢弃无效或孤立的条目,并记录关于被过滤历史的诊断日志。Original summary in English
Summary by Sourcery
Ensure LangChain chat history and tool message handling are robust against invalid tool_call_id values and preserve message metadata for LLM calls.
Bug Fixes:
Enhancements: